package com.alipay.sofa.pay.service;


import com.alibaba.common.lang.StringUtil;
import com.alipay.account.facade.com.alipay.account.dto.AccountDTO;
import com.alipay.sofa.dtx.client.aop.annotation.DtxTransaction;
import com.alipay.sofa.pay.dal.dao.PayOrderDAO;
import com.alipay.sofa.pay.dal.domain.PayOrderDO;
import com.alipay.account.facade.AccountTransService;
import com.alipay.sofa.pay.facade.PayService;
import com.alipay.sofa.pay.facade.model.PayOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.util.*;

@Service("payService")
public class PayServiceImpl implements PayService {

    private final static Logger LOGGER = LoggerFactory.getLogger(PayServiceImpl.class);

    @Autowired
    private PayOrderDAO payOrderDAO;

    @Autowired
    private TransactionTemplate transactionTemplate;

     @Autowired
     private AccountTransService accountTransService;

     @Autowired
     private MqService mqService;
    @Override
    @DtxTransaction(bizType = "payment_codelab")
    public boolean pay(String buyerUid, String sellerUid, Long transAmount) {

        boolean res = false;
        Set<String> orderNos = new HashSet<>();
        try {
            res = (boolean) transactionTemplate.execute(status -> {
                try {
            /*1. 创建订单*/
                    String orderNo = createPayOrder(buyerUid, sellerUid, transAmount);
            /*2. 更新订单状态*/
                    PayOrderDO payOrder = payOrderDAO.queryPayOrderForUpdate(orderNo);
                    if (!payOrderDAO.pay(orderNo)) {
                        LOGGER.error("更新订单状态失败");
                        throw new RuntimeException("更新订单状态失败");
                    }
            /*3. 转账*/
                    if (!accountTransService.doAccount(payOrder.getBuyerId(), payOrder.getSellerId(), payOrder.getPayAmount())) {
                        LOGGER.error("转账失败");
                        throw new RuntimeException("转账失败");
                    }

                    LOGGER.info("转账成功");
                    orderNos.add(orderNo);
                    return true;
                } catch (Exception e) {
                    LOGGER.error("转账异常", e);
                    throw e;
                }
            });
        }catch (Exception e){
            LOGGER.error("do transaction except!",e);
            throw new RuntimeException(e);
        }

        if (res) {
            try {
            /* 给 买家 发送成功消息*/
                PayOrder payOrder = queryPayOrder(orderNos.iterator().next());
                payOrder.setOrderType("OUT");
                payOrder.setTargetUid(payOrder.getBuyerId());
                if (mqService.publish(payOrder))
                    LOGGER.info("发送给[买家]支付消息成功");
                else
                    LOGGER.warn("发送给[买家]支付消息失败");

            /* 给 卖家 发送成功消息*/
                payOrder.setOrderType("IN");
                payOrder.setTargetUid(payOrder.getSellerId());
                if (mqService.publish(payOrder))
                    LOGGER.info("发送给[卖家]支付消息成功");
                else
                    LOGGER.warn("发送给[卖家]支付消息失败");
            }catch (Exception e){
                LOGGER.warn("call mq service except!", e);
            }
        }else {
            throw new RuntimeException("转账异常");
        }
        return res;
    }

    @Override
    public List<PayOrder> listPayOrders(String userId, Integer limit) {
        List<PayOrderDO> payOrderDOS;
        if(StringUtil.isEmpty(userId)) {
            /* query all */
            payOrderDOS = payOrderDAO.listPayOrders();
            Collections.sort(payOrderDOS);
            Collections.reverse(payOrderDOS);
        }else {
            /* query by user id */
            payOrderDOS = payOrderDAO.listPayOrdersByUid(userId);
        }
        if(null != limit && limit >= 0) {
            List<PayOrderDO> limitOrders = new ArrayList<>();
            int max = limit > payOrderDOS.size() ? payOrderDOS.size() : limit;
            for(int i = 0 ; i < max ; i++){
                limitOrders.add(payOrderDOS.get(i));
            }
            payOrderDOS = limitOrders;
        }
        List<PayOrder> payOrders = new ArrayList<>();

        Set<String> uids = new HashSet<>();
        for (PayOrderDO payOrderDO : payOrderDOS) {
            uids.add(payOrderDO.getBuyerId());
            uids.add(payOrderDO.getSellerId());
        }

        Map<String, String> uidNames = getNickNames(uids);

        for (PayOrderDO payOrderDO : payOrderDOS) {
            payOrders.add(convert(payOrderDO, uidNames));
        }
        return payOrders;
    }

    @Override
    public PayOrder queryPayOrder(String orderNo) {
        PayOrderDO payOrderDO = payOrderDAO.queryPayOrder(orderNo);
        Set<String> uids = new HashSet<>();
        uids.add(payOrderDO.getBuyerId());
        uids.add(payOrderDO.getSellerId());

        Map<String, String> uidNames = getNickNames(uids);
        return convert(payOrderDO, uidNames);
    }

    private String createPayOrder(String buyerUid, String sellerUid, Long transAmount) {
        PayOrderDO payOrderDO = new PayOrderDO();
        payOrderDO.setBuyerId(buyerUid);
        payOrderDO.setSellerId(sellerUid);
        payOrderDO.setPayAmount(transAmount);
        payOrderDO.setOrderNo(getOrderNo(buyerUid));

        payOrderDAO.createPayOrder(payOrderDO);
        return payOrderDO.getOrderNo();
    }

    /**
     *
     * @param uid
     * @return 8位时间+8位uuid+uid倒数两位=24位单号
     */
    private String getOrderNo(String uid) {
        SimpleDateFormat dateFormatMS = new SimpleDateFormat("yyyyMMddHHmmss");
        return dateFormatMS.format(new Date())+UUID.randomUUID().toString().substring(0,8)+uid.substring(uid.length()-2);
    }



    private PayOrder convert(PayOrderDO payOrderDO, Map<String, String> uidNames) {
        SimpleDateFormat dateFormatMS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        PayOrder payOrder = new PayOrder();
        payOrder.setBuyerId(payOrderDO.getBuyerId());
        payOrder.setSellerId(payOrderDO.getSellerId());
        payOrder.setStatus(payOrderDO.getStatus());
        payOrder.setOrderNo(payOrderDO.getOrderNo());
        payOrder.setPayAmount(payOrderDO.getPayAmount());
        payOrder.setGmtCreate(dateFormatMS.format(payOrderDO.getGmtCreate()));
        payOrder.setGmtModified(dateFormatMS.format(payOrderDO.getGmtModified()));
        if(!CollectionUtils.isEmpty(uidNames)){
            payOrder.setBuyerNickName(uidNames.get(payOrder.getBuyerId()));
            payOrder.setSellerNickName(uidNames.get(payOrder.getSellerId()));
        }
        return payOrder;
    }

    private Map<String, String> getNickNames(Set<String> uids){
        Map<String, String> uidNames = new HashMap<>();
        if(CollectionUtils.isEmpty(uids))
            return uidNames;
        Iterator iterator = uids.iterator();
        while (iterator.hasNext()){
            AccountDTO accountDTO = accountTransService.getAccount((String) iterator.next(), null);
            if(null != accountDTO){
                uidNames.put(accountDTO.getUserId(), accountDTO.getNickName());
            }
        }
        return uidNames;
    }
}
